MOOD
Overview
The MOOD function performs Mood’s two-sample test for scale parameters, a nonparametric statistical test that determines whether two independent samples are drawn from distributions with equal scale (dispersion). Unlike tests for location (such as means or medians), this test specifically addresses differences in variability or spread between two populations.
The test assumes that the two samples come from probability distributions f(x) and \frac{1}{s}f\left(\frac{x}{s}\right) respectively, where s is the scale parameter. The null hypothesis is that s = 1, meaning both distributions have equal scale. The alternative hypothesis depends on the test direction: two-sided tests detect any difference in scale, while one-sided tests detect whether one sample has greater or lesser dispersion than the other.
Mood’s test is based on ranking all observations from both samples combined. For each observation, it computes the deviation of its rank from the expected median rank, weighted by a specific function. The test statistic is calculated as:
M = \sum_{i=1}^{n_1} \left(R_i - \frac{N+1}{2}\right)^2
where R_i represents the ranks of observations from the first sample in the combined sample, n_1 is the first sample size, and N = n_1 + n_2 is the total number of observations. The test statistic is then standardized to produce a z-score for hypothesis testing.
This implementation uses the SciPy stats.mood function, which returns both the z-score and the corresponding p-value. For detailed information, see the SciPy documentation. Related tests for comparing variances or dispersion include the Ansari-Bradley test, Levene’s test, and Fligner-Killeen test.
This example function is provided as-is without any representation of accuracy.
Excel Usage
=MOOD(x, y, mood_alternative)
x(list[list], required): First sample group of numeric values.y(list[list], required): Second sample group of numeric values.mood_alternative(str, optional, default: “two-sided”): Alternative hypothesis for the test.
Returns (list[list]): 2D list [[statistic, p_value]], or error message string.
Examples
Example 1: Basic two-sided test
Inputs:
| x | y | mood_alternative | ||||
|---|---|---|---|---|---|---|
| 1.2 | 2.3 | 3.4 | 2.1 | 3.2 | 4.3 | two-sided |
| 4.5 | 5.6 | 6.7 | 5.4 | 6.5 | 7.6 |
Excel formula:
=MOOD({1.2,2.3,3.4;4.5,5.6,6.7}, {2.1,3.2,4.3;5.4,6.5,7.6}, "two-sided")
Expected output:
| Result | |
|---|---|
| 0 | 1 |
Example 2: Less alternative hypothesis
Inputs:
| x | y | mood_alternative | ||||
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 10 | 20 | 30 | less |
| 4 | 5 | 6 | 40 | 50 | 60 |
Excel formula:
=MOOD({1,2,3;4,5,6}, {10,20,30;40,50,60}, "less")
Expected output:
| Result | |
|---|---|
| 0 | 0.5 |
Example 3: Greater alternative hypothesis
Inputs:
| x | y | mood_alternative | ||||
|---|---|---|---|---|---|---|
| 10 | 20 | 30 | 1 | 2 | 3 | greater |
| 40 | 50 | 60 | 4 | 5 | 6 |
Excel formula:
=MOOD({10,20,30;40,50,60}, {1,2,3;4,5,6}, "greater")
Expected output:
| Result | |
|---|---|
| 0 | 0.5 |
Example 4: Samples with different scales
Inputs:
| x | y | mood_alternative |
|---|---|---|
| 1 | 0.5 | two-sided |
| 2 | 5 | |
| 3 | 10 | |
| 4 | 15 | |
| 5 | 20 |
Excel formula:
=MOOD({1;2;3;4;5}, {0.5;5;10;15;20}, "two-sided")
Expected output:
| Result | |
|---|---|
| -1.5719 | 0.116 |
Python Code
import math
from scipy.stats import mood as scipy_mood
def mood(x, y, mood_alternative='two-sided'):
"""
Perform Mood's two-sample test for scale parameters.
See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.mood.html
This example function is provided as-is without any representation of accuracy.
Args:
x (list[list]): First sample group of numeric values.
y (list[list]): Second sample group of numeric values.
mood_alternative (str, optional): Alternative hypothesis for the test. Valid options: Two-sided, Less, Greater. Default is 'two-sided'.
Returns:
list[list]: 2D list [[statistic, p_value]], or error message string.
"""
def to2d(val):
return [[val]] if not isinstance(val, list) else val
x = to2d(x)
y = to2d(y)
if not isinstance(x, list) or not all(isinstance(row, list) for row in x):
return "Invalid input: x must be a 2D list."
if not isinstance(y, list) or not all(isinstance(row, list) for row in y):
return "Invalid input: y must be a 2D list."
try:
x_flat = [float(item) for row in x for item in row]
y_flat = [float(item) for row in y for item in row]
except (TypeError, ValueError):
return "Invalid input: x and y must contain only numeric values."
if len(x_flat) < 3 or len(y_flat) < 3:
return "Invalid input: each sample must contain at least three values."
valid_alternatives = ('two-sided', 'less', 'greater')
if mood_alternative not in valid_alternatives:
return f"Invalid input: mood_alternative must be one of {valid_alternatives}."
try:
result = scipy_mood(x_flat, y_flat, alternative=mood_alternative)
stat = float(result.statistic)
pvalue = float(result.pvalue)
except Exception as e:
return f"Mood test error: {e}"
if math.isnan(stat) or math.isinf(stat) or math.isnan(pvalue) or math.isinf(pvalue):
return "Invalid output: result contains NaN or infinite value."
return [[stat, pvalue]]